﻿using spriteeditor;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace ttbit.WorkingSpace
{
    public interface IResDataSource
    {
        bool GetResourceInfo(string name, out string path, out MetaInfo meta);
        string[] GetAllResourceNames();
    }
    public class RefObj<T> where T : class, IDisposable
    {
        T obj;
        int refcount;
        public RefObj(T obj)
        {
            this.obj = obj;
            refcount = 0;
        }
        public T Value
        {
            get
            {
                return this.obj;
            }
        }
        public int RefCount
        {
            get { return refcount; }
        }
        public void AddRef()
        {
            if (this.obj == null)
                throw new Exception("had disposed.");
            refcount++;
        }
        public void DecRef()
        {
            if (this.obj == null)
                throw new Exception("had disposed.");
            refcount--;
            if (refcount == 0)
            {
                this.obj.Dispose();
                this.obj = null;
            }
        }
        public void ForceDispose()
        {
            if (this.obj == null)
                throw new Exception("had disposed.");

            refcount = 0;
            this.obj.Dispose();
            this.obj = null;

        }

    }



    public class ResourceMgr
    {
        public EditorApp editorapp
        {
            get;
            private set;
        }
        IResDataSource datasrc
        {
            get
            {
                return editorapp.workingSpace;
            }
        }
        public ResourceMgr(EditorApp app)
        {
            editorapp = app;
            allparser = new Dictionary<string, IWorkResourceParser>();
            allparser_ext = new Dictionary<string, List<IWorkResourceParser>>();
        }



        public void RegParser(IWorkResourceParser parser)
        {
            allparser.Add(parser.TypeName, parser);

            foreach (var ext in parser.FileExt)
            {
                if (allparser_ext.ContainsKey(ext.ToLower()) == false)
                { allparser_ext[ext.ToLower()] = new List<IWorkResourceParser>(); }
                allparser_ext[ext.ToLower()].Add(parser);
            }
        }
        public IWorkResourceParser[] GetAllParsers()
        {
            return allparser.Values.ToArray();
        }
        public IWorkResourceParser GetParser(string type)
        {
            return allparser[type];
        }
        public IWorkResourceParser[] GetParsersByExt(string ext)
        {
            return allparser_ext[ext].ToArray();
        }
        public IWorkResourceParser GetFirstParserByExt(string ext)
        {
            if (allparser_ext.ContainsKey(ext) == false)
                return null;
            return allparser_ext[ext][0];
        }
        Dictionary<string, IWorkResourceParser> allparser;
        Dictionary<string, List<IWorkResourceParser>> allparser_ext;


        public string[] GetAllResourceNames()
        {
            return datasrc.GetAllResourceNames();
        }
        Dictionary<string, RefObj<IWorkResource>> mapResource = new Dictionary<string, RefObj<IWorkResource>>();
        Dictionary<string, List<string>> mapDepends = new Dictionary<string, List<string>>();


        public List<RefObj<IWorkResource>> GetDependResource(string name)
        {
            List<RefObj<IWorkResource>> depres = new List<RefObj<IWorkResource>>();
            if (mapDepends.TryGetValue(name, out var values))
            {
                foreach (var v in values)
                {
                    var obj = GetResource(v);
                    if (obj != null && obj.Value != null && obj.Value.RefResource.Contains(name))
                    {
                        depres.Add(obj);
                    }
                }
            }
            return depres;
        }
        public RefObj<IWorkResource> GetResource(string name)
        {
            if (mapResource.TryGetValue(name, out var v))
            {
                return v;
            }
            if (datasrc.GetResourceInfo(name, out var path, out var meta))
            {
                var parser = GetParser(meta.Type);
                if (parser != null)
                {
                    var metapath = path + ".meta";
                    using var fsmeta = System.IO.File.OpenRead(metapath);
                    fsmeta.Position = 96;
                    var refobj = new RefObj<IWorkResource>(parser.Load(false, fsmeta, path));
                    UpdateDepends(refobj.Value);
                    mapResource[name] = refobj;
                    return refobj;
                }
            }
            return null;
        }
        public void CloseAll()
        {
            foreach (var res in mapResource)
            {
                if (res.Value.Value != null)
                    res.Value.ForceDispose();
            }
            mapResource.Clear();
            mapDepends.Clear();
        }
        void UpdateDepends(IWorkResource resobj)
        {
            if (resobj.RefResource != null)
            {
                //更新依赖
                foreach (var r in resobj.RefResource)
                {
                    if (mapDepends.ContainsKey(r) == false)
                        mapDepends[r] = new List<string>();
                    var list = mapDepends[r];
                    if (list.Contains(resobj.Name) == false)
                        list.Add(resobj.Name);
                }
            }
        }
        public void ClearUnuseResource()
        {
            List<string> disposed = new List<string>();
            mapDepends.Clear();
            foreach (var res in mapResource)
            {
                if (res.Value.Value == null)
                {
                    disposed.Add(res.Key);
                }
                else if (res.Value.RefCount <= 0)
                {
                    res.Value.ForceDispose();
                    disposed.Add(res.Key);
                }
                else
                {
                    UpdateDepends(res.Value.Value);
                }
            }
            foreach (var res in disposed)
            {
                mapResource.Remove(res);
            }
        }
    }
}
